/*
 *    GeoTools - The Open Source Java GIS Toolkit
 *    http://geotools.org
 *
 *    (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 */
package org.geotools.gml;

import org.xml.sax.SAXException;

/**
 * LEVEL2 saxGML4j GML filter: translates coordinates and GML events into OGC simple types.
 *
 * <p>This filter simply reads in the events and coordinates passed to it by its GMLFilterDocument
 * child and converts them into JTS objects. Note that it passes through anything not specifically
 * sent to it by GMLFilterDocument (i.e. more or less everything not in geometry.xsd). The parent of
 * this filter must implement GMLHandlerJTS in order to receive the JTS objects passed by this
 * filter.
 *
 * @author Rob Hranac, Vision for New York
 * @version $Id$
 */
public class GMLFilterGeometry extends org.xml.sax.helpers.XMLFilterImpl
        implements GMLHandlerGeometry {
    /** Handler for the JTS elements generated by this filter. */
    private GMLHandlerJTS parent;

    /** Factory for the JTS geometries. */
    private org.locationtech.jts.geom.GeometryFactory geometryFactory =
            new org.locationtech.jts.geom.GeometryFactory();

    /** Factory for the GML geometry type subhandlers. */
    private SubHandlerFactory handlerFactory = new SubHandlerFactory();

    /** Generic GML geometry type subhandler. */
    private SubHandler currentHandler;

    /**
     * Constructor with parent, which must implement GMLHandlerJTS.
     *
     * @param parent The parent of this filter.
     */
    public GMLFilterGeometry(GMLHandlerJTS parent) {
        super();
        this.parent = parent;
    }

    /**
     * Manages the start of a new main or sub geometry. This method looks at the status of the
     * current handler and either returns a new sub-handler (if the last one was successfully
     * returned already) or passes the element start notification along to the current handler as a
     * sub geometry notice.
     *
     * @param localName The local name of the geometry, which corresponds to an OGC simple feature
     *     type.
     * @param atts The attributes of the geometry, including SRID, etc.
     * @throws SAXException parser error.
     */
    @Override
    public void geometryStart(String localName, org.xml.sax.Attributes atts) throws SAXException {
        String srs = null;
        for (int i = 0; i < atts.getLength(); i++) {
            final String NAME = atts.getQName(i);
            if ("srs".equalsIgnoreCase(NAME)) {
                srs = atts.getValue(i);
            }
        }
        if (currentHandler == null) {
            currentHandler = handlerFactory.create(localName);
        } else {
            currentHandler.subGeometry(localName, currentHandler.GEOMETRY_START);
        }
        currentHandler.setSRS(srs);
    }

    /**
     * Manages the end of a new main or sub geometry. This method looks at the status of the current
     * handler and either returns the finished JTS object to its parent or passes the element end
     * notification along to the current handler as a sub geometry notice.
     *
     * @param localName The local name of the geometry, which corresponds to an OGC simple feature
     *     type.
     * @throws SAXException parser error.
     */
    @Override
    public void geometryEnd(String localName) throws SAXException {
        if (currentHandler.isComplete(localName)) {
            parent.geometry(currentHandler.create(geometryFactory));
            currentHandler = null;
        } else {
            currentHandler.subGeometry(localName, currentHandler.GEOMETRY_END);
        }
    }

    /**
     * Manages a sub geometry, which simply means always pass it to the current content handler as a
     * sub.
     *
     * @param localName The local name of the geometry, which corresponds to an OGC simple feature
     *     type.
     * @throws SAXException parser error.
     */
    @Override
    public void geometrySub(String localName) throws SAXException {
        currentHandler.subGeometry(localName, currentHandler.GEOMETRY_SUB);
    }

    /**
     * Gets a coordinate from the child and passes it to the current handler as an add request.
     *
     * @param x The X coordinate of the received coordinate.
     * @param y The Y coordinate of the received coordinate.
     * @throws SAXException parser error.
     */
    @Override
    public void gmlCoordinates(double x, double y) throws SAXException {
        currentHandler.addCoordinate(new org.locationtech.jts.geom.Coordinate(x, y));
    }

    /**
     * Gets a coordinate from the child and passes it to the current handler as an add request.
     *
     * @param x The X coordinate of the received coordinate.
     * @param y The Y coordinate of the received coordinate.
     * @param z The Z coordinate of the received coordinate.
     * @throws SAXException parser error.
     */
    @Override
    public void gmlCoordinates(double x, double y, double z) throws SAXException {
        currentHandler.addCoordinate(new org.locationtech.jts.geom.Coordinate(x, y, z));
    }

    /**
     * Checks for GML element start and - if not a coordinates element - sends it directly on down
     * the chain to the appropriate parent handler. If it is a coordinates (or coord) element, it
     * uses internal methods to set the current state of the coordinates reader appropriately.
     *
     * @param namespaceURI The namespace of the element.
     * @param localName The local name of the element.
     * @param qName The full name of the element, including namespace prefix.
     * @param atts The element attributes.
     * @throws SAXException Some parsing error occurred while reading coordinates.
     */
    @Override
    public void startElement(
            String namespaceURI, String localName, String qName, org.xml.sax.Attributes atts)
            throws SAXException {
        parent.startElement(namespaceURI, localName, qName, atts);
    }

    /**
     * Reads the only internal characters read by pure GML parsers, which are coordinates. These
     * coordinates are sent to the coordinates reader class which interprets them appropriately,
     * depending on its current state.
     *
     * @param ch Raw coordinate string from the GML document.
     * @param start Beginning character position of raw coordinate string.
     * @param length Length of the character string.
     * @throws SAXException Some parsing error occurred while reading coordinates.
     */
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        parent.characters(ch, start, length);
    }

    /**
     * Checks for GML element end and - if not a coordinates element - sends it directly on down the
     * chain to the appropriate parent handler. If it is a coordinates (or coord) element, it uses
     * internal methods to set the current state of the coordinates reader appropriately.
     *
     * @param namespaceURI The namespace of the element.
     * @param localName The local name of the element.
     * @param qName The full name of the element, including namespace prefix.
     * @throws SAXException Some parsing error occurred while reading coordinates.
     */
    @Override
    public void endElement(String namespaceURI, String localName, String qName)
            throws SAXException {
        parent.endElement(namespaceURI, localName, qName);
    }
}
